package com.a0soft.gphone.aTruffleHog.wnd.stat;
/*
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 3, or (at your option) any
 * later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * Copyright(C) 2011 
 *   Sam Lu <ysl.sam.lu (at) gmail.com>
 *
 */

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import com.a0soft.gphone.aTruffleHog.R;
import com.a0soft.gphone.aTruffleHog.analytic.AnalyticsUtil;
import com.a0soft.gphone.aTruffleHog.di.DeviceInfo;
import com.a0soft.gphone.aTruffleHog.di.DeviceInfoKey;
import com.a0soft.gphone.aTruffleHog.srvc.HttpConnSrvc;
import com.a0soft.gphone.aTruffleHog.util.CONSTS;
import com.a0soft.gphone.aTruffleHog.util.aDebug;

import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class OsStatsFrg extends BaseStatFragment {
    protected static final int TYPE_MY_DEVICE = 1;
    protected static final int TYPE_ALL_DEVICES = 2;
    protected static final int TYPE_USER_DEFINED = 3;
    
    private ArrayList<OsData>   m_data;
    
    private static final int OTHERS_OS_INT = -1;
    
    private static final String LOG_TAG = OsStatsFrg.class.getSimpleName();
    
    ////////////////////////////////////////
    public static OsStatsFrg 
    NewInstance(Context ctx, int nType, String strMID) 
    {
        OsStatsFrg frg = new OsStatsFrg();
        if (nType == TYPE_MY_DEVICE) {
            DeviceInfo di = DeviceInfo.GetInstance(ctx);
            strMID = di.GetString(DeviceInfoKey.K.ST_MID);
        }
        
        SetTypeAndMID(frg, nType, strMID);
        return frg;
    }
    
    @Override public void 
    onActivityCreated(Bundle bdSavedInstanceState)
    {
        super.onActivityCreated(bdSavedInstanceState);
        if (aDebug.ENABLED) aDebug.d(LOG_TAG, "onActivityCreated(): m_nType=" + m_nType);

        String strPage;
        switch (m_nType) {
        case TYPE_MY_DEVICE: strPage = AnalyticsUtil.PAGE_STAT_OS_MY; break;
        case TYPE_USER_DEFINED: strPage = AnalyticsUtil.PAGE_STAT_OS_USER_DEFINED + "/" + m_strUserDefinedMID; break;
        default: strPage = AnalyticsUtil.PAGE_STAT_OS_ALL; break;
        }
        AnalyticsUtil.GetInstance(getActivity()).TrackPageView(strPage);
    }
    
    @Override public View 
    onCreateView(LayoutInflater inflater, ViewGroup vgContainer,
                 Bundle bdSavedInstanceState) 
    {
        View vRoot = super.onCreateView(inflater, vgContainer, bdSavedInstanceState);
        
        if (!getActivity().isFinishing())
            _GetOsStats(false);
        
        return vRoot;
    }
    
    private void
    _SetData(JSONObject json)
    {
        try {
            m_lDataTime = json.getLong("t");
            
            JSONArray data = json.getJSONArray("d");
            final int N = data.length();
            if (aDebug.ENABLED)
                aDebug.d(LOG_TAG, "N=" + N);
            
            if (N <= 0) {
                m_data = new ArrayList<OsData>(1);
            }
            else {
                m_data = new ArrayList<OsData>(N);
                for (int i = 0; i < N; i++) {
                    JSONObject obj = data.getJSONObject(i);
                    OsData os = new OsData();
                    os.m_fPercentage = (float)obj.getDouble("p");
                    os.m_nAPI = obj.getInt("v");
                    os.m_strVersion = obj.getString("n");
                    os.m_lCount = obj.getLong("c");
                    
                    m_data.add(os);
                }
            }
        }
        catch (JSONException e) {
            if (aDebug.ENABLED)
                aDebug.d(LOG_TAG, "_SetData()", e);
            m_data = new ArrayList<OsData>(1);
        }
        SetStatData(m_data);
        
        Context ctx = getActivity();
        if (ctx == null) return;

        _AddFakeData(ctx);
        _MergeData(ctx);
        _SortData();
        
        DeviceInfo di = DeviceInfo.GetInstance(ctx);
        int nMyDeviceSDK = di.GetInt(DeviceInfoKey.K.ST_SDK_INT);
        String strMyDeviceOS = di.GetString(DeviceInfoKey.K.ST_RELEASE);
        
        String strManufacture = di.GetString(DeviceInfoKey.K.ST_MANUFACTURE);
        String strModel = di.GetString(DeviceInfoKey.K.ST_MODEL);
        if (!strModel.toLowerCase().startsWith(strManufacture.toLowerCase())) {
            strModel = strManufacture + " " + strModel;
        }
        m_strMy = getString(R.string.os_of_your_device, strModel, 
                            strMyDeviceOS, nMyDeviceSDK);
        
        m_nMyIdx = -1;
        final int N = m_data.size();
        for (int i = 0; i < N; i++) {
            OsData os = m_data.get(i);
            if (os.m_nAPI == nMyDeviceSDK &&
                os.m_strVersion.equalsIgnoreCase(strMyDeviceOS)) {
                m_nMyIdx = i;
                break;
            }
        }
        if (m_nMyIdx == -1) {
            //find others
            for (int i = N - 1; i >= 0; i--) {
                OsData os = m_data.get(i);
                if (os.m_nAPI == -1) {
                    m_nMyIdx = i;
                    break;
                }
            }
        }
        
        SetDataFinished();
    }
    
    private void
    _SortData()
    {
        if (m_data.size() <= 1)
            return;
        
        Collections.sort(m_data, new Comparator<OsData>() {
            @Override public int 
            compare(OsData os1, OsData os2)
            {
                if (os1.m_nAPI == OTHERS_OS_INT)
                    return 1;
                if (os2.m_nAPI == OTHERS_OS_INT)
                    return -1;
                int n = os2.m_nAPI - os1.m_nAPI;
                if (n < 0) return -1; else if (n > 0) return 1;
                return os2.m_strVersion.compareTo(os1.m_strVersion);
                /*
                float f = os2.m_fPercentage - os1.m_fPercentage;
                if (f < 0) return -1; else if (f > 0) return 1;
                int n = os2.m_nAPI - os1.m_nAPI;
                if (n < 0) return -1; else if (n > 0) return 1;
                return os1.m_strVersion.compareTo(os2.m_strVersion);
                */
            }
        });
    }
    
    private void
    _MergeData(Context ctx)
    {
        final int N = m_data.size();
        float fMinPercentage = Float.NaN;
        if (N > CONSTS.MAX_PIE_NUMBER) { 
            float[] fPercentages = new float[N];
            for (int i = 0; i < N; i++) {
                OsData os = m_data.get(i);
                fPercentages[i] = os.m_fPercentage;
            }
            Arrays.sort(fPercentages);
            fMinPercentage = fPercentages[N - CONSTS.MAX_PIE_NUMBER + 1];
            if (fMinPercentage < CONSTS.MIN_PIE_PERCENTAGE) {
                fMinPercentage = CONSTS.MIN_PIE_PERCENTAGE;
            }
        }
        else {
            for (OsData os: m_data) {
                if (os.m_fPercentage < CONSTS.MIN_PIE_PERCENTAGE) {
                    fMinPercentage = CONSTS.MIN_PIE_PERCENTAGE;
                    break;
                }
            }
        }
        if (Float.isNaN(fMinPercentage)) return;
        
        //find others and create if not exist
        OsData osOthers = null;
        for (OsData os: m_data) {
            if (os.m_nAPI == OTHERS_OS_INT) {
                osOthers = os;
                break;
            }
        }
        if (osOthers == null) {
            osOthers = new OsData();
            osOthers.m_fPercentage = 0;
            osOthers.m_lCount = 0;
            osOthers.m_nAPI = OTHERS_OS_INT;
            osOthers.m_strVersion = ctx.getString(R.string.others);
            
            m_data.add(osOthers);
        }
        
        //merge
        ArrayList<OsData> osToBeRemoved = new ArrayList<OsData>();
        for (OsData os: m_data) {
            if (os.m_nAPI != OTHERS_OS_INT) {
                if (os.m_fPercentage < fMinPercentage) {
                    osOthers.m_fPercentage += os.m_fPercentage;
                    osOthers.m_lCount += os.m_lCount;
                    osToBeRemoved.add(os);
                }
            }
        }
        m_data.removeAll(osToBeRemoved);
    }
    
    private void
    _AddFakeData(Context ctx)
    {        
        DeviceInfo di = DeviceInfo.GetInstance(ctx);
        final int nDeviceAPI = di.GetInt(DeviceInfoKey.K.ST_SDK_INT);
        final String strAPI = di.GetString(DeviceInfoKey.K.ST_RELEASE);
        
        //no stats, add current device info
        if (m_data.size() == 0) { 
            m_lDataTime = System.currentTimeMillis();
            
            OsData os = new OsData();
            os.m_lCount = 1;
            os.m_fPercentage = 1f;
            os.m_nAPI = nDeviceAPI;
            os.m_strVersion = strAPI;
            
            m_data.add(os);
        }
        
        //only one stat, add others
        if (m_data.size() == 1) { //get 1%
            OsData osOnlyOne = m_data.get(0);
            long lTotal = osOnlyOne.m_lCount;
            
            OsData os = new OsData();
            os.m_fPercentage = osOnlyOne.m_fPercentage * 0.01f;
            os.m_lCount = Math.max(1, (long)(lTotal * 0.01f));
            os.m_nAPI = OTHERS_OS_INT;
            os.m_strVersion = ctx.getString(R.string.others);
            
            m_data.add(os);
            
            osOnlyOne.m_fPercentage -= os.m_fPercentage;
            osOnlyOne.m_lCount -= os.m_lCount;
        }

        //add one if no current device info
        boolean bHasMyDeviceInfo = false;
        long lMaxCount = Long.MIN_VALUE;
        OsData osMax = null;
        for (OsData os: m_data) {
            if (!bHasMyDeviceInfo && 
                os.m_nAPI == nDeviceAPI && 
                os.m_strVersion.equals(strAPI)) {
                bHasMyDeviceInfo = true;
            }
            if (lMaxCount < os.m_lCount) {
                lMaxCount = os.m_lCount;
                osMax = os;
            }
        }
        
        //get 10% from max os
        if (!bHasMyDeviceInfo) {
            m_lDataTime = System.currentTimeMillis();
            
            OsData os = new OsData();
            os.m_fPercentage = 1f / lMaxCount;//osMax.m_fPercentage * 0.1f;
            os.m_lCount = 1; //Math.max(1, (long)(lMaxCount * 0.1f));
            os.m_nAPI = nDeviceAPI;
            os.m_strVersion = strAPI;
            
            m_data.add(os);
            
            osMax.m_fPercentage -= os.m_fPercentage;
            osMax.m_lCount -= os.m_lCount;
        }
    }
    
    private void
    _GetOsStats(boolean bRedownloadFromServer)
    {
        Context ctx = getActivity();
        
        DeviceInfo di = DeviceInfo.GetInstance(ctx);
        final String strDID = di.GetString(DeviceInfoKey.K.UR_ID);
        
        if (m_nType == TYPE_MY_DEVICE || m_nType == TYPE_USER_DEFINED) {
            final String strMID = m_strUserDefinedMID;
            HttpConnSrvc.RequestUserDefinedOsStats(ctx, strMID.hashCode(), 
                                                   strMID, strDID, bRedownloadFromServer);     
        }
        else if (m_nType == TYPE_ALL_DEVICES) {
            HttpConnSrvc.RequestAllOsStats(ctx, strDID, bRedownloadFromServer);  
        }
    }

    @Override protected boolean 
    IsMyDownloadCmd(int nCmd)
    {
        if (m_nType == TYPE_ALL_DEVICES)
            return (nCmd == HttpConnSrvc.CMD_GET_OS_STATS_ALL);
        else if (m_nType == TYPE_MY_DEVICE || m_nType == TYPE_USER_DEFINED)
            return (nCmd == m_strUserDefinedMID.hashCode());
        
        return false;
    }
    
    @Override protected void 
    OnMyDownloadSuccess(int nCmd, JSONObject json)
    {
        super.OnMyDownloadSuccess(nCmd, json);
        
        _SetData(json);
    }
    
    @Override protected void
    SetListItemView(StatData stat, ListItemTagData tag)
    {
        OsData os = (OsData)stat;
        
        tag.m_tvVersion.setText(os.m_strVersion);
        if (os.m_nAPI != OTHERS_OS_INT)
            tag.m_tvAPI.setText(String.format("API: %d", os.m_nAPI));
        else
            tag.m_tvAPI.setText("");
    }
    
    ////////////////////////////////////
    private static class OsData extends StatData {
        private String m_strVersion;
        private int m_nAPI;
    }
}
